use rand::Rng; // trait
use std::cmp::Ordering;
use std::io; // 默认情况下，rust 会把 prelude （序曲的意思，有人将他称作预导入） 模块的内容导入到每个程序的作用域中。
             // 但是你要使用的类型不在 prelude 中，那么你就需要将这个类型显示的导入到程序当中。
             // 使用 use 这个关键字

fn main() {
    println!("猜数！!");

    // 创建随机数，使用 crate 中的库 rand
    // Cargo 本身就能管理依赖包，在 Cargo.toml 中配置这个依赖 rand = "0.3.14"
    // Cargo 会保证依赖的版本，直到手动升级某个版本，所依赖的就是 Cargo.lock 这个文件
    // 想要升级依赖的版本，通过 cargo update 命令，这个命令会忽略 Cargo.lock 这个文件，获取到最新的版本，然后将它写入 Cargo.lock
    // 但是 Cargo.toml 中依赖的版本并没有变
    // 基于语义化版本的规则，比如安装的是 0.4.4 ，那么更新的内容是 0.4.x 版本开始的最新的小版本，可能是 0.4.6 (保证是最新的小版本)
    // 本身安装时也是安装最新的小版本

    // 生成神秘数字
    let secret_number = rand::thread_rng().gen_range(1, 101); // i32 u32 i64
                                                              // println!("生成的神秘数字是: {}", secret_number);

    loop {
        println!("请输入一个1-100的数字: ");

        // rust 中，定义的值默认是不可变的： let foo = 1; 这里的 foo 是不允许修改的，如果 foo = 2; 那么会产生报错；
        // 使用 mut ，表示该数据是可变的。
        let mut guess = String::new();

        // 使用 use std::io; 引入 标准库 std 中的 io -> std::io
        // &mut guess 表示：将 guess 变量的引用作为参数传递，并且是可变的;
        // &guess 表示引用 guess 变量，这样这个传递的参数和 guess 变量使用的是同一个地址；
        // mut 表示该参数是一个可变的。
        // expect 是为了处理错误的
        io::stdin().read_line(&mut guess).expect("无法读取行");

        // guess 生成的是一个字符串，和默认生成的随机数 i32 类型无法进行比较；rust 是一个强类型语言；
        // 对 guess 进行转换，将字符串转换为 u32 类型
        // guess.trim().parse(); trim 是移除字符串首位空格；parse 是转换，返回的是一个 Result 枚举类型，所以它也需要程序崩溃的输出 expect;
        // shadow
        // let guess: u32 = guess.trim().parse().expect("Please type a number!");
        // 由于 parse() 返回的是一个 Result 类型，上面通过 expect 进行错误处理，当对一个 string 类型进行 parse 时，会导致程序奔溃
        // 因为 parse() 返回的是一个 Result 类型，而 Result 类型是一个枚举类型，可以使用 match 进行错误处理
        // 使用 match 代替 expect 处理错误是 rust 中惯用的处理错误的手段
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue, // 当解析出错时，跳出本次循环，继续下次循环
        };

        // {} 表示的是 占位符，它的值在输出的时候，会被替换成后面变量的值。
        // 如果有一个 {} ，则对应后面第一个变量的值，如果有两个 {} ，则按照顺序分别对应第一个和第二个值。
        println!("你猜测的数是: {}", guess);

        // match 表达式，match 需要的是一个枚举类型，内部需要的是一个一个的“手臂”，也就是 arm；
        // guess.cmp 返回的是一个 Ordering 类型
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"), // arm
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => break,
        }
    }
}
